﻿using Microsoft.EntityFrameworkCore;
using Sgr.Application.Queries;
using Sgr.Application.ViewModels;
using System.Linq.Expressions;

namespace Sgr.EntityFrameworkCore
{
    public abstract class EfCoreReadDbContext<TDbContext> : IModuleQueryContext
        where TDbContext : DbContext, IDisposable, IAsyncDisposable
    {
        protected readonly TDbContext _context;
        protected volatile bool _disposed;

        public EfCoreReadDbContext(TDbContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
        }

        #region 查询构建

        public IQueryable<T> Query<T>() where T : class
        {
            ThrowIfDisposed();
            return _context.Set<T>().AsNoTracking();
        }

        public IQueryable<T> QueryWithNavigation<T>(params string[] navigationPropertyPath) where T : class
        {
            ThrowIfDisposed();
            var query = _context.Set<T>().AsNoTracking();
            return navigationPropertyPath.Aggregate(query, (current, path) => current.Include(path));
        }

        #endregion 查询构建

        #region 基础查询

        public Task<T?> FirstOrDefaultAsync<T>(IQueryable<T> query, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.FirstOrDefaultAsync(cancellationToken);
        }

        public Task<T?> SingleOrDefaultAsync<T>(IQueryable<T> query, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.SingleOrDefaultAsync(cancellationToken);
        }

        public async Task<IEnumerable<T>> ToListAsync<T>(IQueryable<T> query, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return await query.ToListAsync(cancellationToken);
        }

        #endregion 基础查询

        #region 高级查询

        public Task<PagedResponse<T>> PaginateAsync<T>(IQueryable<T> query, int skip, int take, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            return query.ToPagedListBySkipAsync(skip, take, cancellationToken);
        }

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task<PagedResponse<T>> PagedListByPageSizeAsync<T>(
            IQueryable<T> query,
             int pageIndex,
             int pageSize,
            CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            return query.ToPagedListByPageSizeAsync(pageIndex, pageSize, cancellationToken);
        }

        #endregion 高级查询

        #region 聚合查询

        public Task<int> CountAsync<T>(IQueryable<T> query, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.CountAsync(cancellationToken);
        }

        public Task<long> LongCountAsync<T>(IQueryable<T> query, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.LongCountAsync(cancellationToken);
        }

        public Task<bool> AnyAsync<T>(IQueryable<T> query, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.AnyAsync(cancellationToken);
        }

        public Task<TResult?> MaxAsync<T, TResult>(IQueryable<T> query, Expression<Func<T, TResult?>> selector, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.MaxAsync(selector, cancellationToken);
        }

        public Task<TResult?> MinAsync<T, TResult>(IQueryable<T> query, Expression<Func<T, TResult?>> selector, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.MinAsync(selector, cancellationToken);
        }

        public Task<decimal?> AverageAsync<T>(IQueryable<T> query, Expression<Func<T, decimal?>> selector, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();
            return query.AverageAsync(selector, cancellationToken);
        }

        #endregion 聚合查询

        #region IDispose

        public void Dispose()
        {
            if (!_disposed)
            {
                _context?.Dispose();
                _disposed = true;
                GC.SuppressFinalize(this);
            }
        }

        public async ValueTask DisposeAsync()
        {
            if (!_disposed)
            {
                await _context.DisposeAsync().ConfigureAwait(false);
                _disposed = true;
                GC.SuppressFinalize(this);
            }
        }

        private void ThrowIfDisposed()
        {
            ObjectDisposedException.ThrowIf(_disposed, GetType().Name);
        }

        #endregion IDispose
    }
}